﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using EzPos.GUI;
using EzPos.Model.Common;
using EzPos.Model.Product;
using EzPos.Model.SaleOrder;
using EzPos.Model.Table;
using EzPos.Properties;
using EzPos.Service;
using EzPos.Service.Common;
using EzPos.Service.Product;
using EzPos.Service.SaleOrder;
using EzPos.Service.Table;
using EzPos.Service.User;
using EzPos.Utility;

namespace EzPos.GUIs.Forms
{
    public partial class FrmSale : Form
    {
        private CommonService _commonService;
        private Product _product;
        private ProductService _productService;
        private TableService _tableService;
        private SaleOrderService _saleOrderService;
        private BindingList<Product> _productList;
        private BindingList<Product> _allProductList;
        public Table Table {get; set;}
        private SaleOrder _saleOrder;
        private BindingList<SaleItem> _saleItemBindingList;
        private bool _adjustSale;

        public string TableInfo { get; set; }
        public bool IsOccupied { get; set; }

        private float _exchangeRate;

        public FrmSale()
        {
            InitializeComponent();
        }

        public CommonService CommonService
        {
            set { _commonService = value; }
        }

        public ProductService ProductService
        {
            set { _productService = value; }
        }

        public bool AdjustSale
        {
            set { _adjustSale = value; }
        }

        public Product Product
        {
            get { return _product; }
            set { _product = value; }
        }       

        private void FrmProductAdvance_FormClosing(object sender, FormClosingEventArgs e)
        {
            IsOccupied = _saleItemBindingList.Count != 0;
        }

        private void FrmProduct_Load(object sender, EventArgs e)
        {
            if(Table == null)
                return;

            if (_productService == null)
                _productService = ServiceFactory.GenerateServiceInstance().GenerateProductService();

            if (_commonService == null)
                _commonService = ServiceFactory.GenerateServiceInstance().GenerateCommonService();

            if (_saleOrderService == null)
                _saleOrderService = ServiceFactory.GenerateServiceInstance().GenerateSaleOrderService();

            if (_tableService == null)
                _tableService = ServiceFactory.GenerateServiceInstance().GenerateTableService();

            //Exchange rate
            _exchangeRate = AppContext.ExchangeRate == null ? 0 : AppContext.ExchangeRate.ExchangeValue;

            InitializeSaleItemList();

            InitializeProductList();

            RetrieveSaleOfTable(Table);

            SetTableHeaderInfo(Table);

            ScanFocusHandler();
        }

        private void SetTableHeaderInfo(Table table)
        {
            lblTableInfo.Text = 
                string.Format("{0} {1}{2}{3} {4}{5}{6} {7}", 
                Resources.ConstPrefixTableName,
                table.TableName,
                "\n",
                Resources.ConstPrefixSaleDate,
                _saleOrder == null ? string.Empty : ((DateTime)_saleOrder.SaleOrderDate).ToString("dd/MM/yyyy HH:mm", AppContext.CultureInfo),
                "\n",
                Resources.ConstPrefixSaleNumber,
                _saleOrder == null ? string.Empty : _saleOrder.SaleOrderNumber);

            //Purchase info
            SetPurchaseInfo(_saleOrder);
        }

        private void InitializeProductList()
        {
            try
            {
                //Product search list
                if (_allProductList == null)
                    _allProductList = new BindingList<Product>();

                dgvProduct.DataSource = _allProductList;
                dgvProduct.Columns["ProductPic"].DisplayIndex = 0;
                dgvProduct.Columns["PublicQty"].DisplayIndex = 1;
                dgvProduct.Columns["PrintCheck"].DisplayIndex = 2;
                dgvProduct.Columns["DisplayName"].DisplayIndex = 3;
                dgvProduct.Columns["Description"].DisplayIndex = 4;
                dgvProduct.Columns["QtyInStock"].DisplayIndex = 5;

                ProductIListToBindingList(_productService.GetObjects());

                //Purchased product list
                _productList = new BindingList<Product>();
                cmbProduct.DataSource = _productList;
                cmbProduct.DisplayMember = Product.ConstProductCode;
                cmbProduct.ValueMember = Product.ConstProductId;
                cmbProduct.SelectedIndex = -1;
            }
            catch (Exception exception)
            {
                FrmExtendedMessageBox.UnknownErrorMessage(
                    Resources.MsgCaptionUnknownError,
                    exception.Message);
            }
        }

        private void ProductIListToBindingList(IEnumerable productList)
        {
            if (productList == null)
                return;

            try
            {
                foreach (var product in productList.Cast<Product>().Where(product => product != null))
                {
                    _allProductList.Add(product);
                }

                DisplayPartialPicture(
                    dgvProduct.DisplayedRowCount(true),
                    0,
                    0,
                    ScrollEventType.LargeIncrement);

            }
            catch (Exception exception)
            {
                FrmExtendedMessageBox.UnknownErrorMessage(
                    Resources.MsgCaptionUnknownError,
                    exception.Message);
            }
        }

        private void DisplayPartialPicture(
            int numPicture,
            int startIndex,
            int stopIndex,
            ScrollEventType scrollEventType)
        {
            try
            {
                if ((scrollEventType == ScrollEventType.SmallDecrement) ||
                    (scrollEventType == ScrollEventType.LargeDecrement))
                {
                    var tmpIndex = startIndex;
                    startIndex = stopIndex;
                    stopIndex = tmpIndex;

                    for (var counter = startIndex + numPicture; counter < stopIndex + numPicture; counter++)
                    {
                        if (counter < _productList.Count)
                            _allProductList[counter].ProductPic = null;
                    }

                    for (var counter = startIndex; counter < startIndex + numPicture; counter++)
                        SetProductPicture(_allProductList[counter]);
                }
                else
                {
                    for (var counter = startIndex; counter < stopIndex; counter++)
                    {
                        _allProductList[counter].ProductPic = null;
                        if (counter >= numPicture)
                            break;
                    }

                    for (var counter = stopIndex; counter < stopIndex + numPicture; counter++)
                        SetProductPicture(_allProductList[counter]);
                }
                dgvProduct.Refresh();
            }
            catch (Exception exception)
            {
                FrmExtendedMessageBox.UnknownErrorMessage(
                    Resources.MsgCaptionUnknownError,
                    exception.Message);
            }
        }

        private static void SetProductPicture(Product product)
        {
            if (product.ProductPic != null)
                return;

            product.ProductPic = Resources.NoImage;
            if (String.IsNullOrEmpty(product.PhotoPath))
                return;

            var fileInfo = new FileInfo(product.PhotoPath);
            product.ProductPic = fileInfo.Exists ? new Bitmap(product.PhotoPath) : Resources.NoImage;
        }

        private void InitializeSaleItemList()
        {
            if (_saleItemBindingList == null)
                _saleItemBindingList = new SaleItemBindingList<SaleItem>();

            AdjustColumOrder();
            dgvSaleItem.DataSource = _saleItemBindingList;
            _saleItemBindingList.ListChanged += SaleItemBindingListChanged;
        }

        private void SaleItemBindingListChanged(object sender, ListChangedEventArgs e)
        {
            var isEnabled = _saleItemBindingList.Count != 0;
            SetEnableToComponents(isEnabled);

            //Calculate purchase amount
            PurchasedAmountHandler(ref _saleOrder, _saleItemBindingList);
        }

        private void AutoSaveInvoice(Table table, ref SaleOrder saleOrder, SaleItem saleItem, string requestCode)
        {
            if(string.IsNullOrEmpty(requestCode))
                return;

            if(table == null)
                return;

            if (saleItem == null)
                return;
            
            //SaleOrder
            if ((saleOrder == null) && (requestCode.Equals(Resources.OperationRequestInsert)))
            {
                saleOrder = 
                    new SaleOrder
                    {
                        CustomerId = 1,
                        SaleOrderDate = DateTime.Now,
                        CashierId = AppContext.User.UserId,
                        ExchangeRate = AppContext.ExchangeRate.ExchangeValue
                    };
                _saleOrderService.SaleOrderManagement(saleOrder, Resources.OperationRequestInsert);
            }
            
            //SaleItem
            if(saleOrder == null)
                return;

            saleItem.SaleOrderId = saleOrder.SaleOrderId;
            _saleOrderService.SaleItemManagement(saleItem, requestCode);

            //Table status: Block
            Table.StatusId = 1;
            Table.SaleOrderId = saleOrder.SaleOrderId;
            _tableService.TableManagement(Table, Resources.OperationRequestUpdate);
        }

        private void RetrieveSaleOfTable(Table table)
        {
            IList saleItemList = null;
            RetrieveSale(table, ref _saleOrder, ref saleItemList);
            IListToBindingList(saleItemList);

            //Calculate purchase amount
            PurchasedAmountHandler(ref _saleOrder, _saleItemBindingList);
        }

        private void RetrieveSale(Table table, ref SaleOrder saleOrder, ref IList saleItemList)
        {
            if (table == null)
                return;

            var searchCriteriaList = new List<string> { "SaleOrderId|" + table.SaleOrderId };
            var saleOrderList = _saleOrderService.GetSaleOrders(searchCriteriaList);

            if (saleOrderList == null)
                return;

            if (saleOrderList.Count == 0)
                return;

            saleOrder = saleOrderList[0] as SaleOrder;
            if (saleOrder == null)
                return;

            saleItemList = _saleOrderService.GetSaleItems(saleOrder.SaleOrderId);
        }

        private void AdjustColumOrder()
        {
            if (dgvSaleItem == null)
                return;

            dgvSaleItem.Columns["DelColumn"].DisplayIndex = 0;
            dgvSaleItem.Columns["ProdPicture"].DisplayIndex = 1;
            dgvSaleItem.Columns["ColProductName"].DisplayIndex = 2;
            dgvSaleItem.Columns["ColQtySold"].DisplayIndex = 4;
            dgvSaleItem.Columns["ColQtyBonus"].DisplayIndex = 5;
            dgvSaleItem.Columns["PublicUPOut"].DisplayIndex = 9;
            dgvSaleItem.Columns["Discount"].DisplayIndex = 10;
            dgvSaleItem.Columns["SubTotal"].DisplayIndex = 11;
        }

        private void IListToBindingList(IList saleItemList)
        {
            if (saleItemList == null)
                return;

            try
            {
                _saleItemBindingList.Clear();
                
                cmbProduct.SelectedIndexChanged -= CmbProductSelectedIndexChanged;
                _saleItemBindingList.ListChanged -= SaleItemBindingListChanged;

                foreach (SaleItem saleItem in saleItemList)
                {
                    var product = saleItem.FkProduct;

                    //Purchased product list
                    _productList.Add(product);

                    //Invoice item
                    var localSaleItem = saleItem;
                    CreateSaleItem(
                        ref localSaleItem,
                        product,
                        saleItem.QtySold,
                        saleItem.Discount);

                    _saleItemBindingList.Add(localSaleItem);
                }

                cmbProduct.SelectedIndexChanged += CmbProductSelectedIndexChanged;
                _saleItemBindingList.ListChanged += SaleItemBindingListChanged;

                SetEnableToComponents(_saleItemBindingList.Count != 0);
            }
            catch (Exception exception)
            {
                FrmExtendedMessageBox.UnknownErrorMessage(
                    Resources.MsgCaptionUnknownError,
                    exception.Message);
            }
        }

        private static float CalculateQtyBonus(int purchasedQty, int promotionQty, int bonusQty)
        {
            var returnQty = 0f;

            if (promotionQty != 0)
                returnQty = (purchasedQty / promotionQty) * bonusQty;

            return returnQty;
        }

        private void CreateSaleItem(ref SaleItem saleItem, Product product, int qtySold, float discount)
        {
            if (saleItem == null)
                saleItem = new SaleItem();

            saleItem.ProductName = product.ProductName;
            saleItem.ProductId = product.ProductId;
            saleItem.QtySold = qtySold;
            saleItem.QtyBonus = 
                CalculateQtyBonus(
                    qtySold,  
                    Int32.Parse(product.QtyPromotion.ToString("N0", AppContext.CultureInfo)), 
                    Int32.Parse(product.QtyBonus.ToString("N0", AppContext.CultureInfo)));
            saleItem.UnitPriceIn = product.UnitPriceIn;
            saleItem.ProductDisplayName =
                string.IsNullOrEmpty(product.Description) ? 
                product.ProductName + "\r" + product.ProductCode : 
                product.Description + "\r" + product.ProductCode;

            if (!string.IsNullOrEmpty(product.ForeignCode))
                saleItem.ProductDisplayName += " (" + product.ForeignCode + ")";

            var unitPriceIn =
                float.Parse(Math.Round(product.UnitPriceIn, 3).ToString("N", AppContext.CultureInfo),
                            AppContext.CultureInfo);

            var publicUnitPriceOut =
                unitPriceIn + ((unitPriceIn * product.ExtraPercentage) / 100);

            saleItem.UnitPriceOut =
                float.Parse(
                    publicUnitPriceOut.ToString("N", AppContext.CultureInfo),
                    AppContext.CultureInfo);
            publicUnitPriceOut =
                publicUnitPriceOut -
                ((publicUnitPriceOut * discount) / 100);

            if (publicUnitPriceOut == product.UnitPriceIn)
                publicUnitPriceOut = product.UnitPriceOut;

            publicUnitPriceOut =
                float.Parse(
                    publicUnitPriceOut.ToString("N", AppContext.CultureInfo),
                    AppContext.CultureInfo);

            saleItem.UnitPriceOut = publicUnitPriceOut;
            saleItem.PublicUpOut = publicUnitPriceOut;
            saleItem.Discount = discount;
            saleItem.SubTotal =
                saleItem.QtySold * publicUnitPriceOut;

            if (string.IsNullOrEmpty(product.PhotoPath))
                saleItem.ProdPicture = Resources.NoImage;
            else
            {
                var fileInfo = new FileInfo(product.PhotoPath);
                saleItem.ProdPicture = fileInfo.Exists ? Image.FromFile(product.PhotoPath) : Resources.NoImage;
            }
            cmbProduct.SelectedIndex = -1;
            saleItem.FkProduct = product;
        }

        private void SetEnableToComponents(bool enableStatus)
        {
            btnPayment.Enabled = enableStatus && !_adjustSale;
            btnCancel.Enabled = enableStatus;
             
            btnAdjustSale.Enabled = _adjustSale;
        }

        private void DoProductFetching(string productCode, bool isAllowed)
        {
            if (String.IsNullOrEmpty(productCode))
                return;

            cmbProduct.SelectedIndex = -1;
            var selectedIndex = cmbProduct.FindStringExact(
                StringHelper.Right("000000000" + productCode, 9));

            //Switch to foreign code
            if (selectedIndex == -1)
            {
                cmbProduct.SelectedIndexChanged -= CmbProductSelectedIndexChanged;
                cmbProduct.DisplayMember = Product.ConstForeignCode;

                selectedIndex = cmbProduct.FindStringExact(productCode);

                //Switch to product code
                cmbProduct.DisplayMember = Product.ConstForeignCode;
                cmbProduct.SelectedIndex = -1;
                cmbProduct.SelectedIndexChanged += CmbProductSelectedIndexChanged;
            }
            cmbProduct.SelectedIndex = selectedIndex;

            if ((selectedIndex != -1) || (!isAllowed))
                return;

            var strCriteria =
                new List<string>
                {
                    "(ProductCode = '" + 
                    StringHelper.Right("000000000" + productCode, 9) + 
                    "' OR ForeignCode = '" + productCode + "')"
                };

            var productList = _productService.GetObjects(strCriteria);
            if (productList.Count != 0)
            {
                foreach (Product product in productList)
                    _productList.Add(product);

                DoProductFetching(productCode, false);
            }
            else
            {
                txtHidden.Text = string.Empty;
                const string briefMsg = "អំពីផលិតផល";
                var detailMsg = Resources.MsgOperationRequestProductNotFound;
                using (var frmMessageBox = new FrmExtendedMessageBox())
                {
                    frmMessageBox.BriefMsgStr = briefMsg;
                    frmMessageBox.DetailMsgStr = detailMsg;
                    frmMessageBox.IsCanceledOnly = true;
                    frmMessageBox.ShowDialog(this);
                    return;
                }
            }
        }        

        private void SpcBodyPanel1SizeChanged(object sender, EventArgs e)
        {
            var panelWidth = spcBody.Panel1.Width;
            dgvProduct.Width = panelWidth - 26;
        }

        private void CmbProductSelectedIndexChanged(object sender, EventArgs e)
        {
            if ((String.IsNullOrEmpty(cmbProduct.DisplayMember)) ||
                (String.IsNullOrEmpty(cmbProduct.ValueMember)) ||
                (cmbProduct.SelectedIndex == -1))
                return;

            try
            {
                var product = (Product)cmbProduct.SelectedItem;
                if (product == null)
                    return;

                var foundFlag = false;
                var selectedIndex = 0;
                dgvSaleItem.SelectionChanged -= DgvSaleItemSelectionChanged;
                foreach (var saleItem in _saleItemBindingList)
                {
                    if (product.ProductId == saleItem.ProductId)
                    {
                        foundFlag = true;

                        saleItem.QtySold += 1;
                        saleItem.QtyBonus =
                            CalculateQtyBonus(
                                saleItem.QtySold,
                                Int32.Parse(product.QtyPromotion.ToString("N0", AppContext.CultureInfo)),
                                Int32.Parse(product.QtyBonus.ToString("N0", AppContext.CultureInfo)));

                        saleItem.SubTotal = float.Parse(
                            (saleItem.QtySold * product.UnitPriceOut).ToString("N", AppContext.CultureInfo),
                            AppContext.CultureInfo);

                        AutoSaveInvoice(Table, ref _saleOrder, saleItem, Resources.OperationRequestUpdate);
                        break;
                    }
                    selectedIndex += 1;
                }

                if (!foundFlag)
                {
                    SaleItem saleItem = null;
                    CreateSaleItem(ref saleItem, product, 1, product.DiscountPercentage);
                    _saleItemBindingList.Add(saleItem);
                    AutoSaveInvoice(Table, ref _saleOrder, saleItem, Resources.OperationRequestInsert);
                }

                dgvSaleItem.Refresh();
                UpdateSelectedIndex(selectedIndex);
                dgvSaleItem.SelectionChanged += DgvSaleItemSelectionChanged;

                PurchasedAmountHandler(ref _saleOrder, _saleItemBindingList);

                ScanFocusHandler();
            }
            catch (Exception exception)
            {
                FrmExtendedMessageBox.UnknownErrorMessage(
                    Resources.MsgCaptionUnknownError,
                    exception.Message);
            }
        }

        private void UpdateSelectedIndex(int selectedIndex)
        {
            if (_saleItemBindingList != null)
            {
                if (selectedIndex < 0)
                {
                    if (_saleItemBindingList.Count == 1)
                    {
                        return;
                    }
                    selectedIndex = 0;
                }

                if (selectedIndex == _saleItemBindingList.Count)
                    return;
            }

            dgvSaleItem.Rows[selectedIndex].Selected = true;

            if (selectedIndex < dgvSaleItem.FirstDisplayedScrollingRowIndex)
                dgvSaleItem.FirstDisplayedScrollingRowIndex = selectedIndex;
            else if ((selectedIndex - dgvSaleItem.FirstDisplayedScrollingRowIndex) < 0)
                dgvSaleItem.FirstDisplayedScrollingRowIndex -= 1;
            else
            {
                if (selectedIndex >=
                    (dgvSaleItem.DisplayedRowCount(true) + dgvSaleItem.FirstDisplayedScrollingRowIndex - 1))
                {
                    if (_saleItemBindingList != null)
                        if ((selectedIndex - dgvSaleItem.DisplayedRowCount(true) + 2) >=
                            _saleItemBindingList.Count)
                            return;

                    dgvSaleItem.FirstDisplayedScrollingRowIndex =
                        selectedIndex - dgvSaleItem.DisplayedRowCount(true) + 2;
                }
            }
        }

        private void DgvSaleItemSelectionChanged(object sender, EventArgs e)
        {
            if (_saleItemBindingList == null)
                return;

            if (_saleItemBindingList.Count == 0)
                return;

            if (dgvSaleItem.CurrentRow != null)
            {
                if (_saleItemBindingList[dgvSaleItem.CurrentRow.Index].FkProduct != null)
                {
                    //SetProductInfo(
                    //    _saleItemBindingList[dgvSaleItem.SelectedRows[0].Index].FkProduct);
                }
                else
                {
                    cmbProduct.SelectedIndexChanged -= CmbProductSelectedIndexChanged;
                    if (_productList.Any(product => product.ProductId == Int32.Parse(dgvSaleItem.Rows[dgvSaleItem.SelectedRows[0].Index].Cells["ProductId"].Value.ToString(), AppContext.CultureInfo)))
                    {
                        cmbProduct.SelectedIndex = -1;
                    }
                    cmbProduct.SelectedIndexChanged += CmbProductSelectedIndexChanged;
                }
            }

            ScanFocusHandler();
        }

        private void KeyDownHandler(object sender, KeyEventArgs e)
        {
            if (e == null)
                return;

            try
            {
                switch (e.KeyCode)
                {
                    case Keys.Escape:
                        DialogResult = DialogResult.Abort;
                        break;
                    case Keys.Delete:
                        if (_saleItemBindingList.Count == 0)
                            return;
                        UpdateSaleItem(Resources.OperationRequestDelete,
                                       dgvSaleItem.SelectedRows[0].Index);
                        break;
                    case Keys.Add:
                        if (_saleItemBindingList.Count == 0)
                            return;
                        
                        UpdateSaleItem(Resources.OperationRequestInsert,
                                       dgvSaleItem.SelectedRows[0].Index);
                        break;
                    case Keys.Subtract:
                        if (_saleItemBindingList.Count == 0)
                            return;

                        UpdateSaleItem(Resources.OperationRequestUpdate,
                                       dgvSaleItem.SelectedRows[0].Index);
                        break;
                    case Keys.Up:
                        if (_saleItemBindingList.Count == 0)
                            return;
                        UpdateSelectedIndex(dgvSaleItem.SelectedRows[0].Index - 1);
                        break;
                    case Keys.Down:
                        if (_saleItemBindingList.Count == 0)
                            return;
                        UpdateSelectedIndex(dgvSaleItem.SelectedRows[0].Index + 1);
                        break;
                    case Keys.Return:
                        DoProductFetching(txtHidden.Text, true);
                        break;
                    case Keys.F1:                        
                        var splitterDistance = spcBody.SplitterDistance;
                        spcBody.SplitterDistance = splitterDistance != 350 ? 350 : 5;
                        break;
                    default:
                        break;
                }
            }
            catch (Exception exception)
            {
                FrmExtendedMessageBox.UnknownErrorMessage(
                    Resources.MsgCaptionUnknownError,
                    exception.Message);
            }            
        }

        private void UpdateSaleItem(string requestType, int selectedIndex)
        {
            ScanFocusHandler();
            if (requestType == Resources.OperationRequestDelete)
            {
                UpdateSelectedIndex(selectedIndex - 1);

                var saleItem = _saleItemBindingList[selectedIndex];
                AutoSaveInvoice(Table, ref _saleOrder, saleItem, requestType);

                _saleItemBindingList.RemoveAt(selectedIndex);
            }
            else
            {
                var saleItem = _saleItemBindingList[selectedIndex];
                if (requestType == Resources.OperationRequestInsert)
                {
                    if (_productList.Any(product => (product.ProductId == saleItem.ProductId) && (product.QtyInStock == saleItem.QtySold)))
                        return;

                    saleItem.QtySold += 1;
                }
                else
                {
                    if (saleItem.QtySold != 1)
                        saleItem.QtySold -= 1;
                }

                var purchasedProduct = saleItem.FkProduct;
                saleItem.QtyBonus =
                    CalculateQtyBonus(
                        saleItem.QtySold,
                        Int32.Parse(purchasedProduct.QtyPromotion.ToString("N0", AppContext.CultureInfo)),
                        Int32.Parse(purchasedProduct.QtyBonus.ToString("N0", AppContext.CultureInfo)));

                saleItem.SubTotal = saleItem.QtySold * saleItem.UnitPriceOut;

                _saleOrderService.SaleItemManagement(saleItem, Resources.OperationRequestUpdate);
            }
            dgvSaleItem.Refresh();

            PurchasedAmountHandler(ref _saleOrder, _saleItemBindingList);
        }

        private void ScanFocusHandler()
        {
            txtHidden.Text = string.Empty;
            if (txtHidden.CanFocus)
                txtHidden.Focus();

            cmbProduct.SelectedIndex = -1;
        }

        private void DgvSaleItemCellClick(object sender, DataGridViewCellEventArgs e)
        {
            ScanFocusHandler();

            if (_saleItemBindingList == null)
                return;

            if (_saleItemBindingList.Count == 0)
                return;

            if (dgvSaleItem.CurrentCell.ColumnIndex == 0)
                UpdateSaleItem(Resources.OperationRequestDelete, dgvSaleItem.SelectedRows[0].Index);
        }

        private void DgvProductScroll(object sender, ScrollEventArgs e)
        {
            DisplayPartialPicture(
                dgvProduct.DisplayedRowCount(true),
                e.OldValue,
                e.NewValue,
                e.Type);
        }

        private void DgvProductSelectionChanged(object sender, EventArgs e)
        {
            if ((_allProductList.Count == 0) || (dgvProduct.CurrentRow == null))
            {
                return;
            }

            _product = _allProductList[dgvProduct.CurrentRow.Index];
        }

        private void DgvProductDoubleClick(object sender, EventArgs e)
        {
            if (dgvProduct.CurrentRow == null)
                return;

            var product = _allProductList[dgvProduct.CurrentRow.Index];
            DoProductFetching(product.ProductCode, true);
        }

        private void BtnPaymentClick(object sender, EventArgs e)
        {
            //Payment
            using (var frmPayment = new FrmPayment())
            {
                frmPayment.DiscountPercentage = _saleOrder.Discount;
                frmPayment.Table = Table;
                frmPayment.SaleOrder = _saleOrder;
                frmPayment.SaleItemList = _saleItemBindingList;

                if(frmPayment.ShowDialog(this) == DialogResult.OK)
                {
                    //Print invoice
                    ScanFocusHandler();
                    if (_saleItemBindingList.Count == 0)
                        return;

                    try
                    {
                        //Validate
                        _saleOrder = frmPayment.SaleOrder;
                        _saleOrderService.SaleOrderManagement(_saleOrder, Resources.OperationRequestUpdate);

                        _saleOrderService.RecordSaleOrderReport(_saleOrder, _saleItemBindingList.ToList());

                        //Table status: Free
                        Table.StatusId = 0;
                        Table.SaleOrderId = 0;
                        _tableService.TableManagement(Table, Resources.OperationRequestUpdate);

                        //Thank form
                        var frmThank = new FrmThank();
                        frmThank.ShowDialog(this);

                        DialogResult = DialogResult.OK;
                    }
                    catch (Exception exception)
                    {
                        FrmExtendedMessageBox.UnknownErrorMessage(
                            Resources.MsgCaptionUnknownError,
                            exception.Message);
                    }                    
                }
                else
                    SetPurchaseInfo(_saleOrder);
            }
        }

        private void BtnCancelClick(object sender, EventArgs e)
        {
            if (Table == null)
                return;

            string briefMsg, detailMsg;
            if (!UserService.AllowToPerform(Resources.PermissionReturnProduct))
            {
                briefMsg = "អំពី​សិទ្ឋិ​ប្រើ​ប្រាស់";
                detailMsg = Resources.MsgUserPermissionDeny;
                using (var frmMessageBox = new FrmExtendedMessageBox())
                {
                    frmMessageBox.BriefMsgStr = briefMsg;
                    frmMessageBox.DetailMsgStr = detailMsg;
                    frmMessageBox.IsCanceledOnly = true;
                    frmMessageBox.ShowDialog(this);
                    return;
                }
            }

            briefMsg = "អំពី​ការ​លក់";
            detailMsg = Resources.MsgCancelSale;
            using (var frmMessageBox = new FrmExtendedMessageBox())
            {
                frmMessageBox.BriefMsgStr = briefMsg;
                frmMessageBox.DetailMsgStr = detailMsg;
                frmMessageBox.IsCanceledOnly = false;
                if (frmMessageBox.ShowDialog(this) != DialogResult.OK)
                    return;
            }

            try
            {
                //Payment
                //_saleOrder.SaleOrderTypeId = 1;
                _saleOrderService.SaleOrderManagement(_saleOrder, Resources.OperationRequestCancel);

                //Table status: Free
                Table.StatusId = 0;
                Table.SaleOrderId = 0;
                _tableService.TableManagement(Table, Resources.OperationRequestUpdate);

                //Close form
                DialogResult = DialogResult.OK;
            }
            catch(Exception exception)
            {
                FrmExtendedMessageBox.UnknownErrorMessage(
                    Resources.MsgCaptionUnknownError,
                    exception.Message);                
            }
        }

        private void PurchasedAmountHandler(ref SaleOrder saleOrder, IEnumerable<SaleItem> saleItemList)
        {
            if(saleOrder == null)
                return;

            if(saleItemList == null)
                return;

            var totalAmountLocal = (
                from saleItem 
                in saleItemList 
                select (saleItem.UnitPriceOut * saleItem.QtySold)).Sum();

            saleOrder.AmountSoldLocal = totalAmountLocal;

            var totalAmountForeign = totalAmountLocal / _exchangeRate;
            saleOrder.AmountSoldForeign = totalAmountForeign;

            //Update purchase info
            SetPurchaseInfo(saleOrder);
        }

        private void SetPurchaseInfo(SaleOrder saleOrder)
        {
            if(saleOrder == null)
                return;

            var totalAmountLocal =
                saleOrder.AmountSoldLocal - ((saleOrder.AmountSoldLocal * saleOrder.Discount)/100);
            
            var purchaseInfo =
                saleOrder.AmountSoldLocal.ToString("N0", AppContext.CultureInfo) + " :" + Resources.ConstTotalAmountPrefix + "\n" +
                saleOrder.Discount.ToString("N0", AppContext.CultureInfo) + " % :" + Resources.ConstDiscountPrefix + "\n" +
                totalAmountLocal.ToString("N0", AppContext.CultureInfo) + " :" + Resources.ConstToPayAmountPrefix;

            lblPurchaseInfo.Text = purchaseInfo;
        }

        private void BtnAdjustSaleClick(object sender, EventArgs e)
        {
            //Payment
            using (var frmPayment = new FrmPayment())
            {
                frmPayment.DiscountPercentage = _saleOrder.Discount;
                frmPayment.Table = Table;
                frmPayment.SaleOrder = _saleOrder;
                frmPayment.SaleItemList = _saleItemBindingList;
                frmPayment.AdjustSale = _adjustSale;

                if (frmPayment.ShowDialog(this) == DialogResult.OK)
                {
                    //Print invoice
                    ScanFocusHandler();
                    if (_saleItemBindingList.Count == 0)
                        return;

                    try
                    {
                        //Cancel current invoice
                        _saleOrder.SaleOrderTypeId = 1;
                        _saleOrderService.SaleOrderManagement(
                            _saleOrder,
                            Resources.OperationRequestCancel);

                        //Register new invoice
                        _saleOrder = frmPayment.SaleOrder;
                        _saleOrder.SaleOrderId = 0;
                        _saleOrder.SaleOrderTypeId = 0;
                        _saleOrder.SaleOrderDate = DateTime.Now;
                        _saleOrder.ReferenceNum = _saleOrder.SaleOrderNumber;
                        _saleOrderService.SaleOrderManagement(_saleOrder, Resources.OperationRequestInsert);

                        foreach (var saleItem in _saleItemBindingList.Where(saleItem => saleItem != null))
                        {
                            saleItem.SaleItemId = 0;
                            saleItem.SaleOrderId = _saleOrder.SaleOrderId;
                            _saleOrderService.SaleItemManagement(saleItem, Resources.OperationRequestInsert);
                        }                        

                        _saleOrderService.RecordSaleOrderReport(_saleOrder, _saleItemBindingList.ToList());

                        //Thank form
                        var frmThank = new FrmThank();
                        frmThank.ShowDialog(this);

                        DialogResult = DialogResult.OK;
                    }
                    catch (Exception exception)
                    {
                        FrmExtendedMessageBox.UnknownErrorMessage(
                            Resources.MsgCaptionUnknownError,
                            exception.Message);
                    }
                }
                else
                    SetPurchaseInfo(_saleOrder);
            }

        }
    }
}